﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using Douban.Core.Helpers;
using System.Windows.Media.Animation;

namespace Douban.Controls
{
    [TemplatePart(Name = "PART_WindowButtonGroup", Type = typeof(Panel))]
    [TemplatePart(Name = "PART_Header", Type = typeof(Panel))]
    [TemplatePart(Name = "PART_TopResizeBar", Type = typeof(UIElement))]
    [TemplatePart(Name = "PART_ButtomResizeBar", Type = typeof(UIElement))]
    [TemplatePart(Name = "PART_LeftResizeBar", Type = typeof(UIElement))]
    [TemplatePart(Name = "PART_RightResizeBar", Type = typeof(UIElement))]
    public class BorderlessWindow : Window
    {
        private double _windowButtomWhenResizing;
        private double _windowRightWhenResizing;
        private bool _preparedForResizing;
        private const double DefaultHiddenBarThickness = 4.0;

        static BorderlessWindow()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(BorderlessWindow),
                new FrameworkPropertyMetadata(typeof(BorderlessWindow)));
        }

        public BorderlessWindow()
        {
            Loaded += BorderlessWindow_Loaded;
            WindowButtonGroup = new WindowButtonCollection();
        }

        #region DependencyProperties

        public static readonly RoutedEvent AllModulesLoadedEvent = EventManager.RegisterRoutedEvent("AllModulesLoaded", 
            RoutingStrategy.Direct, typeof(RoutedEventHandler), typeof(BorderlessWindow));

        public event RoutedEventHandler AllModulesLoaded
        {
            add { AddHandler(AllModulesLoadedEvent, value); }
            remove { RemoveHandler(AllModulesLoadedEvent, value); }
        }

        public double ResizeBarThickness
        {
            get { return (double)GetValue(ResizeBarThicknessProperty); }
            set { SetValue(ResizeBarThicknessProperty, value); }
        }

        public static readonly DependencyProperty ResizeBarThicknessProperty =
            DependencyProperty.Register("ResizeBarThickness", typeof(double), typeof(BorderlessWindow), 
                new UIPropertyMetadata((double)5));

        public object Header
        {
            get { return GetValue(HeaderProperty); }
            set { SetValue(HeaderProperty, value); }
        }

        public static readonly DependencyProperty HeaderProperty =
            DependencyProperty.Register("Header", typeof(object), typeof(BorderlessWindow), new UIPropertyMetadata(null));

        public object Footer
        {
            get { return (object)GetValue(FooterProperty); }
            set { SetValue(FooterProperty, value); }
        }

        public static readonly DependencyProperty FooterProperty =
            DependencyProperty.Register("Footer", typeof(object), typeof(BorderlessWindow), new UIPropertyMetadata(null));


        public bool Resizable
        {
            get { return (bool)GetValue(ResizableProperty); }
            set { SetValue(ResizableProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Resizable.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ResizableProperty =
            DependencyProperty.Register("Resizable", typeof(bool), typeof(BorderlessWindow), new UIPropertyMetadata(true));

        

        #endregion    

        #region Private Methods

        private void BorderlessWindow_Loaded(object sender, RoutedEventArgs e)
        {
            InitialTemplatePart();
        }

        private void InitialTemplatePart()
        {
            InitialHeader();
            InitialWindowButtons();

            if(Resizable) InitialResizeBar();
        }

        private void InitialHeader()
        {
            var header = FindWindowTemplateChild<Panel>("PART_Header");
            header.MouseLeftButtonDown += (sender, e) => this.DragMove();
        }

        private void InitialResizeBar()
        {
            var buttomResizeBar = FindWindowTemplateChild<UIElement>("PART_ButtomResizeBar");

            if (buttomResizeBar != null)
            {
                buttomResizeBar.MouseLeftButtonDown += ResizeBar_InitialResize;
                buttomResizeBar.MouseLeftButtonUp += ResizeBar_EndResize;
                buttomResizeBar.MouseMove += ResizeBar_VerticalResize;
            }

            var topResizeBar = FindWindowTemplateChild<UIElement>("PART_TopResizeBar");

            if(topResizeBar != null)
            {
                topResizeBar.MouseLeftButtonDown += topResizeBar_InitialResize;
                topResizeBar.MouseLeftButtonUp += ResizeBar_EndResize;
                topResizeBar.MouseMove += ResizeBar_VerticalResize;
            }

            var leftResizeBar = FindWindowTemplateChild<UIElement>("PART_LeftResizeBar");

            if (leftResizeBar != null)
            {
                leftResizeBar.MouseLeftButtonDown += leftResizeBar_InitialResize;
                leftResizeBar.MouseLeftButtonUp += ResizeBar_EndResize;
                leftResizeBar.MouseMove += ResizeBar_HorizontalResize;
            }

            var rightResizeBar = FindWindowTemplateChild<UIElement>("PART_RightResizeBar");

            if (rightResizeBar != null)
            {
                rightResizeBar.MouseLeftButtonDown += ResizeBar_InitialResize;
                rightResizeBar.MouseLeftButtonUp += ResizeBar_EndResize;
                rightResizeBar.MouseMove += ResizeBar_HorizontalResize;
            }
        }

        private void ResizeBar_InitialResize(object sender, MouseButtonEventArgs e)
        {
            _preparedForResizing = true;
        }

        private void topResizeBar_InitialResize(object sender, MouseButtonEventArgs e)
        {
            _preparedForResizing = true;
            _windowButtomWhenResizing = this.Top + this.ActualHeight;
        }

        private void leftResizeBar_InitialResize(object sender, MouseButtonEventArgs e)
        {
            _preparedForResizing = true;
            _windowRightWhenResizing = this.Left + this.ActualWidth;
        }

        private void ResizeBar_EndResize(object sender, MouseButtonEventArgs e)
        {
            _preparedForResizing = false;

            UIElement uiElement = (UIElement)sender;
            uiElement.ReleaseMouseCapture();

            _windowButtomWhenResizing = -1;
            _windowRightWhenResizing = -1;
        }

        private void ResizeBar_VerticalResize(object sender, MouseEventArgs e)
        {
            if (e.LeftButton != MouseButtonState.Pressed || !_preparedForResizing) return;

            UIElement uiElement = (UIElement)sender;
            uiElement.CaptureMouse();

            double newHeight = 0;
            
            if(_windowButtomWhenResizing > 0)
            {
                Point cursorPos = new Point();
                GetCursorPos(ref cursorPos);
                var mousePositionY = cursorPos.Y;
                newHeight = _windowButtomWhenResizing - mousePositionY;

                if (newHeight > 0 && newHeight > MinHeight && newHeight < MaxHeight)
                    this.Top = mousePositionY;
            }
            else
            {
                newHeight = e.GetPosition(this).Y + ResizeBarThickness;
            }

            if (newHeight > 0 && newHeight > MinHeight && newHeight < MaxHeight) 
                this.Height = newHeight;
        }

        private void ResizeBar_HorizontalResize(object sender, MouseEventArgs e)
        {
            if (e.LeftButton != MouseButtonState.Pressed || !_preparedForResizing) return;

            UIElement uiElement = (UIElement)sender;
            uiElement.CaptureMouse();

            double newWidth = 0;

            if (_windowRightWhenResizing > 0)
            {
                Point cursorPos = new Point();
                GetCursorPos(ref cursorPos);
                var mousePositionX = cursorPos.X;
                newWidth = _windowRightWhenResizing - mousePositionX;

                if (newWidth > 0 && newWidth > MinWidth && newWidth < MaxWidth)
                    this.Left = mousePositionX;
            }
            else
            {
                newWidth = e.GetPosition(this).X;
            }

            if (newWidth > 0 && newWidth > MinWidth && newWidth < MaxWidth)
                this.Width = newWidth;
        }

        private void InitialWindowButtons()
        {
            Panel windowButtonPart = FindWindowTemplateChild<Panel>("PART_WindowButtonGroup");

            if (windowButtonPart == null) return;

            foreach (var windowButton in WindowButtonGroup)
                windowButtonPart.Children.Add(windowButton);
        }

        private TChildItem FindWindowTemplateChild<TChildItem>(string name) where TChildItem : DependencyObject
        {
            TChildItem childItem = Template.FindName(name, this) as TChildItem;

            return childItem;
        }

        #endregion

        #region Properties

        public WindowButtonCollection WindowButtonGroup { get; set; }

        #endregion

        #region P/Invoke

        [DllImport("user32.dll")]
        internal static extern bool GetCursorPos(ref Point pt);

        internal struct Point
        {
            public int X;
            public int Y;

            public Point(int x, int y)
            {
                X = x;
                Y = y;
            }
        }

        #endregion
    }
}
